﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace HowItWentDown
{
    /*This will be the basis for ALL objects
     * within the game world. */
    public class Obj
    {
        public Boolean stationary = false, floating = false, collision = false, climbable = false, pickable = false, lookAtCamera = false, clickable = true, held = false; //Boolean Variables: Stationary, can it budge? Floating, is it affected by gravity? 
            //Collision, does it have a physical space? Climbable, can you climb it? Pickable, can you pick it up?
        /// <summary>
        /// Object's vertical acceleration
        /// </summary>
        /// 
        public bool visible = true;
        public bool highlight = false;
        public float gravity; //drop of object
        protected Game1 doc;
        // the object's posin the world, has X, Y, and Z. Also the Object Velocity
        public Vector3 pos, velocity;
        // the origin of the sprite, has X and Y, basically the center of where it is drawn, you'll see when you start using it
        Vector2 screenPos = Vector2.Zero;
        /// <summary>
        /// the object's sprite (called a texture), it's static for now, will add a separate class for animated objects later
        /// </summary>
        public Texture2D tex;
        // the object's scale, with X and Y
        public Vector2 Scale = Vector2.One;
        // a multiplyer for the object's scale
        public float scale = 1f;
        /// <summary>
        /// how much the object's scale should increase or decrease in each frame
        /// </summary>
        public float scalespeed = 0;
        // the scale the object is drawn it, this is relative to the amount of zoom on the camera
        private Vector2 drawScale = Vector2.One;
        // zFactor is basically a number that is calculated based on the difference between the object's z-posand the camera's z-position
        float zFactor = 1;
        // depth is just a something extra to use to sort how the objects are drawn when they are at the same z-position
        public float depth = 0;
        // alpha is the object's transparency, 0 is fully transparent, and 1 is fully opaque
        public float alpha = 1;
        // a Vector3 for rotation along the X, Y and Z axes
        public Vector3 rot = Vector3.Zero;
        /// <summary>
        /// Rotaitonal speed, added in each update
        /// </summary>
        public Vector3 rotspeed = Vector3.Zero;
        public BoundingBox collisionBox;
        public PolygonRect rect;
        public float bloom = 0;
        // Dirty hack to check if object is a tree
        public Boolean isTree = false;
        /// <summary>
        /// Color the object will be drawn at when using the CustomEffect shader.
        /// </summary>
        public Color Color
        {
            get { return col; }
            set
            {
                rect.Color = col = value;
            }
        }
        Color col = Color.White;
        public Color HighlightColor = Color.White;
        public float x
        {
            get { return pos.X; }
            set { pos.X = value; }
        }
        public float y
        {
            get { return pos.Y; }
            set { pos.Y = value; }
        }
        public float z
        {
            get { return pos.Z; }
            set { pos.Z = value; }
        }

        /*overloading the constructor for the sake of making new objects easily
         *the first is just a blank contructor 
         *9mainly for compiling issues
         *second does not take in any booleans
         *third is the full contructor
         **/
        public Obj() { }

        public Obj(Game1 Doc, float X, float Y, float Z)
        {
            doc = Doc;
            pos = new Vector3(X, Y, Z);
            // each new object is added to the Game1 class' list of objects
            doc.AddObj(this);
            rect = new PolygonRect(10, 10, null);
        }
        /*
        public Obj(Game1 Doc, float X, float Y, float Z, Texture2D texture)
        {
            doc = Doc;
            tex = texture;
            pos = new Vector3(X, Y, Z);
            rect = new PolygonRect(10, 10, texture);
            // each new object is added to the Game1 class' list of objects
            doc.AddObj(this);
        }*/

        public Obj(Game1 Doc, float X, float Y, float Z, Texture2D texture, Boolean stat = true, Boolean coll = true, Boolean clim = false, Boolean pick = false, Boolean floa = false)
        {
            doc = Doc;
            tex = texture;
            pos = new Vector3(X, Y, Z);

            //boolean for 
            stationary = stat;
            floating = floa;
            collision = coll;
            climbable = clim;
            pickable = pick;
            rect = new PolygonRect(texture.Width / 10, texture.Height / 10, texture);
            collisionBox = new BoundingBox(new Vector3(pos.X, pos.Y, pos.Z - 5), new Vector3(pos.X + (rect.Width * scale), pos.Y + (rect.Height * scale), pos.Z + 5));
            // each new object is added to the Game1 class' list of objects
            doc.AddObj(this);
        }
        //updates for whatever reason (movement, action, etc.)
        public virtual void earlyUpdate()
        {
            
        }
        /// <summary>
        /// Gets whether or not the mouse is over the object. If the object is not clickable, this will always return false
        /// </summary>
        /// <returns></returns>
        public virtual bool IsMouseOver()
        {
            if (clickable)
            {
                // Gets screen position of the object
                Vector2 screenPos = GetScreenPosition();
                // Gets how big the object is on screen (Objects further away will be smaller, so we want a smaller clickable area for them)
                float screenScale = GetScreenScale();
                // Gets the position of the mouse relative to the object's screen position
                Vector2 mPos = screenPos - Controls.MousePos;
                // Creates a rectangle that is the size of the object on screen
                Rectangle r = new Rectangle(0, 0, (int)(rect.Width * scale * screenScale * Camera.FieldOfView / 2f), (int)(rect.Height * scale * screenScale * Camera.FieldOfView / 2f));
                // Returns whether or not the mouse position is within the bounds of the rectangle
                return (r.Contains((int)mPos.X, (int)mPos.Y));
            }
            else
            {
                return false;
            }
        }
        public virtual void Update()
        {
            rot += rotspeed * Global.Speed;
            scale += scalespeed * Global.Speed;
            if (!(stationary))
            {
                if (!held)
                {
                    velocity.Y -= gravity * Global.Speed;
                    pos += velocity * Global.Speed;
                }
            }
            collisionBox = new BoundingBox(new Vector3(pos.X, pos.Y, pos.Z - 10), new Vector3(pos.X + (rect.Width * scale), pos.Y + (rect.Height * scale), pos.Z + 10));

            //if targetted, change color
            if (doc.target == this)
            {

                if (this is Enemy)
                {
                    HighlightColor = Color.Crimson;
                }
                else if (climbable)
                {
                    HighlightColor = Color.Blue;
                }
                else if (pickable)
                {
                    HighlightColor = Color.Green;
                }
                highlight = true;
            }
            else
            {
                highlight = false;
                HighlightColor = Color.White;
            }


        }

        public void obstacleCollision()
        {
            foreach (Obj collisionObject in doc.objList)
            {
                if (collisionObject.isTree)
                {
                    if (this.collisionBox.Intersects(collisionObject.collisionBox))
                    {
                        pos -= velocity * Global.Speed;
                    }
                }
            }
        }
        public virtual void lateUpdate()
        {
            
        }
        public virtual T Collides<T>() where T : Obj
        {
            T obj = null;
            foreach (Obj o in doc.objList)
            {
                if (o.collision && o is T && o.collisionBox.Intersects(collisionBox))
                {
                    obj = (T)o;
                }
            }
            return obj;
        }
        public virtual bool CollidesWith(Obj o)
        {
            return o.collision && collisionBox.Intersects(o.collisionBox);
        }
        /// <summary>
        /// Returns the objects screen position, instead of its world position
        /// </summary>
        /// <returns></returns>
        public Vector2 GetScreenPosition()
        {
            Matrix mat = Matrix.Identity * doc.customEffect.View * doc.customEffect.Projection;
            var v4 = Vector4.Transform(pos - (rect.Origin * scale), mat);
            return new Vector2((v4.X / v4.W + 1) * 640, ((1 - v4.Y / v4.W)) * 360);
        }
        /// <summary>
        /// Returns the object's scale on screen
        /// </summary>
        /// <returns></returns>
        public float GetScreenScale()
        {
            Matrix mat = Matrix.Identity * doc.customEffect.View * doc.customEffect.Projection;
            var v4 = Vector4.Transform(pos, mat);
            return Camera.FieldOfView/v4.W;
        }
        /// <summary>
        /// Overridable method for removing the object from the array of objects in the Game1 class
        /// </summary>
        public virtual void remove()
        {
            doc.RemoveObj(this);
        }
        public virtual void Draw()
        {
            if (lookAtCamera)
            {
                Vector2 rv;
                // Get the angle between the camera and the object
                rv = MyMath.Direction(pos, Camera.Pos);
                doc.customEffect.World = Matrix.CreateRotationX(MathHelper.ToRadians(rot.X)) * Matrix.CreateRotationY(rv.X + MathHelper.Pi) * Matrix.CreateRotationZ(MathHelper.ToRadians(rot.Z)) * Matrix.CreateScale(scale) * Matrix.CreateTranslation(pos);
            }
            else
            doc.customEffect.World = Matrix.CreateRotationX(MathHelper.ToRadians(rot.X)) * Matrix.CreateRotationY(MathHelper.ToRadians(rot.Y)) * Matrix.CreateRotationZ(MathHelper.ToRadians(rot.Z)) * Matrix.CreateScale(scale) * Matrix.CreateTranslation(pos - (rect.Origin * scale));
            doc.customEffect.Texture = tex; doc.customEffect.Alpha = alpha;
            doc.customEffect.Color = Color;
            doc.customEffect.Highlight = highlight;
            doc.customEffect.HighlightColor = HighlightColor;
            doc.customEffect.Bloom = bloom;
            doc.customEffect.UpdateTechnique();
            // Apply all passes of the basicEffect, which sends the drawing parameters to the graphics card
            foreach (EffectPass ep in doc.customEffect.CurrentTechnique.Passes)
            {
                //if ((ep.Name == "OutlinePass1") || (highlight))
                {
                    ep.Apply();
                    // Draw the polygons within each pass.
                    doc.graphics.GraphicsDevice.DrawUserPrimitives<VertexPositionColorTexture>(PrimitiveType.TriangleList, rect.vertices, 0, 2);
                }

            }
        }
            
    }
}
